package com.pashanhoo.Thread.queue;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 请结合ReentrantLock、Condition实现一个简单的阻塞队列，阻塞队列提供两个方法，一个是put、一个是take
 * 1.当队列为空时，请求take会被阻塞，直到队列不为空
 * 2.当队列满了以后，存储元素的线程需要备阻塞直到队列可以添加数据
 * @author 胡庆安
 * @version V1.0
 * @Package com.pashanhoo.Thread
 * @date 2020/11/24 9:29
 * @Copyright © 2010-2020 爬山虎科技（扬州）股份有限公司
 */
@Slf4j
public class HqaQueue {
    private int maxSize;
    private Node node = null;
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public HqaQueue(int size){
        this.maxSize = size;
    }
    public Object take(){
        log.info("我来取数据了");
        lock.lock();
        try {
            while (node == null){
                try {
                    log.info("我来取数据的时候被阻塞了");
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            Node next = node.next;
            Node temp = node;
            if (next == null){
                node = null;
                log.info("我取完数据了");
                return temp;
            }
            node = next;
            log.info("我取完数据了");
            condition.signal();
            return temp;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return null;
    }
    public void put(Object o){
        log.info("我来存数据了");
        lock.lock();
        try {
            while (getSize()>= maxSize){
                try {
                    log.info("我来存数据的时候被阻塞了");
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (node !=null){
                Node next = new Node(o);
                next.pre = node;
                node.next = next;
                log.info("我存完数据了");
                condition.signal();
                return;
            }
            node = new Node(o);
            log.info("我存完数据了");
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public int getSize(){
        int result = 0;
        Node temp = node;
        if (node !=null){
            while (true){
                if (temp == null){
                    break;
                }
                result++;
                temp = node.next;
            }
        }
        return result;
    }


    class Node<T>{
        Node<T> next;
        Node<T> pre;
        Object o;
        Node(Object o){
            this.o = o;
        }
    }
}
